/* This software was developed at the National Institute of Standards and
 * Technology by employees of the Federal Government in the course of
 * their official duties. Pursuant to title 17 Section 105 of the United
 * States Code this software is not subject to copyright protection and
 * is in the public domain.
 * NIST assumes no responsibility whatsoever for its use by other parties,
 * and makes no guarantees, expressed or implied, about its quality,
 * reliability, or any other characteristic.
 * <BR>
 * We would appreciate acknowledgement if the software is used.
 * <BR>
 * NIST ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
 * DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING
 * FROM THE USE OF THIS SOFTWARE.
 * </PRE></P>
 * @author  rouil
 */

#include "ssscheduler.h"
#include "burst.h"
#include "mac802_16SS.h"

/**
 * Tcl hook for creating SS scheduler 
 */
static class SSschedulerClass : public TclClass {
public:

    SSschedulerClass() : TclClass("WimaxScheduler/SS") {
    }

    TclObject* create(int, const char*const*) {
        return (new SSscheduler());

    }
} class_ssscheduler;

/*
 * Create a scheduler
 */
SSscheduler::SSscheduler() {
    debug2("SSscheduler created\n");
}

/**
 * Initializes the scheduler
 */
void SSscheduler::init() {
    WimaxScheduler::init();
}

/**
 * Interface with the TCL script
 * @param argc The number of parameter
 * @param argv The list of parameters
 */
int SSscheduler::command(int argc, const char*const* argv) {
    //no command. Remove this function if not used.
    return TCL_ERROR;
}

/**
 * @input The original bandwidth request
 */

double SSscheduler::makeup(double input)
{
    double corrected = input;
    return corrected;
}

/**
 * Schedule bursts/packets
 */
void SSscheduler::schedule() {
    //printf("HERE %f\n",NOW);
    Burst *b;
    FrameMap *map = mac_->getMap();
    PeerNode *peer = mac_->getPeerNode_head(); //this is the BS
    flow_record *flowRecord;
    assert(peer != NULL);

    //We go through the list of UL bursts until we find an allocation for us
    //printf ("SS has %d ul bursts %f \n", map->getUlSubframe()->getNbPdu (),NOW);
    mac_->clearBurst();
    for (int index = 0; index < map->getUlSubframe()->getNbPdu(); index++) {
        b = map->getUlSubframe()->getPhyPdu(index)->getBurst(0);
        //printf ("\tBurst CID=%d\t%f\n", b->getCid(),NOW);
        if (b->getIUC() == UIUC_END_OF_MAP) {
            //consistency check..
            assert(index == map->getUlSubframe()->getNbPdu() - 1);
            break;
        }

        if (b->getIUC() == UIUC_INITIAL_RANGING || b->getIUC() == UIUC_REQ_REGION_FULL)
            continue; //contention slots are handled automatically

        int b_data = 0;
        //get the packets from the connection with the same CID
        Connection *c = mac_->getCManager()->get_connection(b->getCid(), true);
        //assert (c);
        if (!c)
            continue; //I do not have this CID. Must be for another node
        if (c->getType() == CONN_BASIC) { //this slot is for management message
            if (peer->getBasic(OUT_CONNECTION) != NULL)
                b_data = mac_->getScheduler()->transfer_packets(peer->getBasic(OUT_CONNECTION), b, b_data);
            if (peer->getPrimary(OUT_CONNECTION) != NULL)
                b_data = mac_->getScheduler()->transfer_packets(peer->getPrimary(OUT_CONNECTION), b, b_data);
            if (peer->getSecondary(OUT_CONNECTION) != NULL) {
                b_data = mac_->getScheduler()->transfer_packets(peer->getSecondary(OUT_CONNECTION), b, b_data);
            }
        } else { //c->getType() = CONN_DATA
            
            //printf("burst right now is %d\n", flowRecord->burst);
            //transfer the packets until it reaches burst duration or no more packets
            //fprintf(stderr,"%d\n",c->getType());
            assert(c->getType() == CONN_DATA);
            assert(peer->getOutData(c->get_cid()));
            //printf("%d\n",b->getDuration());

            //TODO: need to do more check here.
            //if (b->getDuration() == ) continue;
            flowRecord = &(mac_->UpflowTable[mac_->findFlowRecord(c)]);
            flowRecord->burst++;
            
            //@todo: need to implement bandwidth request estimate for rtPS, Chris

            if (b->getIUC() == UIUC_REQ_REGION_FOCUSED) { // unicast polling
                //printf("\033[1;32mhere at polling, time right now is %f, queue right now is %d\033[m\n", NOW, c->queueByteLength());
                Packet *p = mac_->getPacket();
                hdr_cmn* ch = HDR_CMN(p);
                bw_req_header_t *header = (bw_req_header_t *)&(HDR_MAC802_16(p)->header);
                //unsigned int offset = mac_->getScheduler()->estimateBW(flowRecord, c);
                header->ht = 1;
                header->ec = 1;
                header->type = 1; //aggregate
                //header->br = c->queueByteLength() + offset;
                //printf("bandwidth request = %d + %d\n\n", c->queueByteLength(), offset);
                //header->br = c->queueByteLength() + (int)((double)c->queueByteLength() / 4); // /100*20
                header->br = (int)(makeup((double)c->queueByteLength()));
                header->cid = c->get_cid();
                //fprintf(stdout,"%d at %f\n",con->get_cid(),NOW);

                double txtime = mac_->getPhy()->getTrxTime(ch->size(), mac_->getMap()->getUlSubframe()->getProfile(UIUC_REQ_REGION_FOCUSED)->getEncoding());
                ch->txtime() = txtime;
                b->enqueue(p);
                //continue;
            } else {
                b_data = mac_->getScheduler()->transfer_packets(c, b, b_data);
//                if( c->get_serviceflow()->getScheduling() == SERVICE_rtPS )
//                    printf("\033[0;35mAt %f put %d data in burst\n\033[m",NOW, b_data);
            }
            //statistical use
            //c->get_serviceflow()->setScheduling(flowRecord->qosRecord->getScheduling());
        }

    }

    //compute size of data left to create bandwidth requests
    if (peer->getBasic(OUT_CONNECTION) != NULL)
        create_request(peer->getBasic(OUT_CONNECTION));
    if (peer->getPrimary(OUT_CONNECTION) != NULL)
        create_request(peer->getPrimary(OUT_CONNECTION));
    if (peer->getSecondary(OUT_CONNECTION) != NULL)
        create_request(peer->getSecondary(OUT_CONNECTION));
    if (!peer->getOutDataList()->empty()) { //only BE and NRTPS require bandwidth via contention slot
        for (unsigned int i = 0; i < peer->getOutDataList()->size(); i++) {
            SchedulingType_t s_type = peer->getOutDataList()->at(i)->get_serviceflow()->getScheduling();
            if (s_type == SERVICE_BE || s_type == SERVICE_nrtPS)
                create_request(peer->getOutDataList()->at(i));
        }
    }
}

/**
 * Create a request for the given connection
 * This function may need to be updated to handle
 * incremental and aggregate requests
 * @param con The connection to check
 */
void SSscheduler::create_request(Connection *con) {
    //fprintf(stdout,"flow_cid %d queuelength %d\n",con->get_cid(),con->queueLength());
    if (con->getType() == CONN_DATA)
        assert(con->get_serviceflow()->getScheduling() == SERVICE_BE || con->get_serviceflow()->getScheduling() == SERVICE_nrtPS);
    if (con->queueLength() == 0)
        return; //queue is empty
    else if (mac_->getMap()->getUlSubframe()->getBw_req()->getRequest(con->get_cid()) != NULL) {
        debug2("At %f in Mac %d already pending requests for cid=%d\n", NOW, mac_->addr(), con->get_cid());
        return; //there is already a pending request
    }

    Packet *p = mac_->getPacket();
    hdr_cmn* ch = HDR_CMN(p);
    bw_req_header_t *header = (bw_req_header_t *)&(HDR_MAC802_16(p)->header);
    header->ht = 1;
    header->ec = 1;
    header->type = 1; //aggregate
    header->br = con->queueByteLength();
    header->cid = con->get_cid();
    //fprintf(stdout,"%d at %f\n",con->get_cid(),NOW);

    double txtime = mac_->getPhy()->getTrxTime(ch->size(), mac_->getMap()->getUlSubframe()->getProfile(UIUC_REQ_REGION_FULL)->getEncoding());
    ch->txtime() = txtime;
    mac_->getMap()->getUlSubframe()->getBw_req()->addRequest(p, con->get_cid(), con->queueByteLength());
    //printf("SSscheduler enqueued request for cid=%d len=%d (nbPacket=%d)\n", con->get_cid(), con->queueByteLength(), con->queueLength());
}
